////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
//  File name:   SoundBufferFmodEx400Event.cpp
//  Version:     v1.00
//  Created:     28/07/2005 by Tomas.
//  Compilers:   Visual Studio.NET
//  Description: FMODEX 4.00 Implementation of Event SoundBuffer
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"

#ifdef SOUNDSYSTEM_USE_FMODEX400

#include <ITimer.h>
#include "SoundBufferFmodEx400Event.h"
#include "AudioDeviceFmodEx400.h"
//#include "SoundSystem.h"
#include "Sound.h"
#include <FmodEx/inc/fmod_errors.h>
#include <CrySizer.h>

//////////////////////////////////////////////////////////////////////////
#define IS_FMODERROR (m_ExResult != FMOD_OK )

CSoundBufferFmodEx400Event::CSoundBufferFmodEx400Event(const SSoundBufferProps &Props, FMOD::System* pCSEX) : 
CSoundBuffer(Props)
{
	m_pCSEX									= pCSEX;
	m_ExResult							= FMOD_OK;
	m_Props.nFlags					= Props.nFlags;
	m_Props.nPrecacheFlags	= Props.nPrecacheFlags;
	m_nGroupIndex						= -1;
}

CSoundBufferFmodEx400Event::~CSoundBufferFmodEx400Event()
{
	//if (m_pReadStream != NULL)
	//{
	//	m_pReadStream->Abort();	
	//	m_pReadStream = NULL;
	//}
	UnloadData(sbUNLOAD_ALL_DATA_NOW);

	// Tell Sounds that their soundbuffers are invalid now
	m_bCallbackGuard = true;
	for (size_t reqIdx = 0; reqIdx < m_vecLoadReq.size(); ++ reqIdx)
	{
		CSound* pSound = (CSound*) m_vecLoadReq[reqIdx];
		pSound->OnBufferDelete(); 
	}
	m_bCallbackGuard = false;
	UpdateCallbacks();
}


void CSoundBufferFmodEx400Event::FmodErrorOutput(const char * sDescription, ESoundLogType LogType)
{
	gEnv->pSoundSystem->Log(LogType, "<Sound> SBE: (%s) %s \n", m_Props.sName.c_str(), sDescription);
	gEnv->pSoundSystem->Log(LogType, "<Sound> SBE: (%d) %s\n", m_ExResult, FMOD_ErrorString(m_ExResult));
}

uint32 CSoundBufferFmodEx400Event::GetMemoryUsed()
{
	size_t nSize = sizeof(*this);

	if (!m_BufferData)
		return nSize;

	FMOD::EventGroup *pEventGroup = (FMOD::EventGroup*)m_BufferData;

	int nMemUsed = 0;
	int nNumEvents = 0;
	m_ExResult = pEventGroup->getNumEvents(&nNumEvents);
	if (IS_FMODERROR)
	{
		FmodErrorOutput("get group numevents failed! ", eSLT_Warning);
		return nSize;
	}

	unsigned int memoryused = 0;

	m_ExResult = pEventGroup->getMemoryInfo(FMOD_MEMBITS_SOUND, 0, &memoryused, 0);

	nMemUsed += memoryused;

	// on querying sound data from an event group, we dont have iterate through all events and collect individual
	// size, as this would double the result. I leave the code here for times when structural memory usage is tracked
	/*
	for (int i=0; i<nNumEvents; ++i)
	{
		FMOD::Event *pEvent = 0;
		m_ExResult = pEventGroup->getEventByIndex(i, FMOD_EVENT_INFOONLY, &pEvent);

		if (IS_FMODERROR)
		{
			FmodErrorOutput("get event by index failed! ", eSLT_Warning);
			return nSize;
		}

		unsigned int eventmemoryused = 0;

		m_ExResult = pEvent->getMemoryInfo(FMOD_MEMBITS_SOUND, 0, &eventmemoryused, 0);

		if (IS_FMODERROR)
		{
			FmodErrorOutput("get event memory info failed! ", eSLT_Warning);
			return nSize;
		}

		nMemUsed += eventmemoryused;
	}
	*/
	

	return nMemUsed;
}

// compute memory-consumption, returns size in Bytes
void CSoundBufferFmodEx400Event::GetMemoryUsage(class ICrySizer* pSizer) const
{
	pSizer->AddObject( this, sizeof(*this) );	
	CSoundBuffer::GetMemoryUsage(pSizer);
}

void CSoundBufferFmodEx400Event::LogDependencies()
{
	if (!m_BufferData)
		return;

	FMOD::EventGroup *pEventGroup = (FMOD::EventGroup*)m_BufferData;
	int nNumEvents = 0;

	m_ExResult = pEventGroup->getNumEvents(&nNumEvents);

	if (IS_FMODERROR)
	{
		FmodErrorOutput("create group num. events failed! ", eSLT_Warning);
		return;
	}

	for (int i=0; i<nNumEvents; ++i)
	{
		FMOD::Event *pEvent = 0;
		{
			CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_BUFFER_OTHER) );
			m_ExResult = pEventGroup->getEventByIndex(i, FMOD_EVENT_INFOONLY, &pEvent);
		}

		if (IS_FMODERROR)
		{
			FmodErrorOutput("get event by index failed! ", eSLT_Warning);
			break;
		}

		FMOD_EVENT_INFO EventInfo;


		// first get number of wavebanks
		m_ExResult = pEvent->getInfo(0, 0, &EventInfo);

		if (IS_FMODERROR)
		{
			FmodErrorOutput("event system get event info failed! ", eSLT_Warning);
			return;
		}

		FMOD_EVENT_WAVEBANKINFO* pWavebankInfo = NULL;

		if (EventInfo.maxwavebanks)
		{
			// now repeat to fill the array of wavebank infos
			pWavebankInfo = new FMOD_EVENT_WAVEBANKINFO[EventInfo.maxwavebanks];

			if (!pWavebankInfo)
				return;

			EventInfo.wavebankinfo = pWavebankInfo;

			m_ExResult = pEvent->getInfo(0, 0, &EventInfo);

			if (IS_FMODERROR)
			{
				FmodErrorOutput("event system get event info with wavebanks failed! ", eSLT_Warning);
				delete[] pWavebankInfo; // remove wavebankinfo array
				return;
			}
		}

		for (int nBankIndex=0; nBankIndex<EventInfo.maxwavebanks; ++nBankIndex)
		{
			// Compose full File name to Wavebank
			TFixedResourceName sWavebankName;
			TFixedResourceName sFilePath = m_Props.sName;
			const char *pInput = sFilePath;
			const char *pColon = strstr(pInput, ":");

			if (pInput && pColon)
				{
					sFilePath.assign(sFilePath.substr(0, pColon - pInput));
					sFilePath += "/";

					sWavebankName.assign(pWavebankInfo[nBankIndex].name, strlen(pWavebankInfo[nBankIndex].name));
					sWavebankName += ".fsb";

					sWavebankName = sFilePath + sWavebankName;

					if (m_pSoundSystem->g_nDebugSound > 1)
						CryLog(" <Dependency> %s ", sWavebankName.c_str());

					FILE* pFile = gEnv->pCryPak->FOpen(sWavebankName.c_str(), "rbx");

					if (pFile)
						gEnv->pCryPak->FClose(pFile);

				}
		}

		delete[] pWavebankInfo; // remove wavebankinfo array
	}
}


// loads a event sound
tAssetHandle CSoundBufferFmodEx400Event::LoadAsEvent(const char *AssetName)
{
	FUNCTION_PROFILER( gEnv->pSystem,PROFILE_SOUND );

	assert(m_Props.nProjectIndex != PROJECTINDEX_INVALID);

	FMOD::EventGroup* pEventGroup = NULL;

	if (m_Props.nProjectIndex != PROJECTINDEX_INVALID)
	{
		bool const bCacheEvents = false;
		if (m_pSoundSystem->g_nProfiling == 0)
		{
			CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_BUFFER_OTHER) );
			pEventGroup = GetEventGroup(AssetName, bCacheEvents);
		}
		else
		{
			float f0 = gEnv->pTimer->GetAsyncCurTime();
			CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_BUFFER_OTHER) );
			pEventGroup = GetEventGroup(AssetName, bCacheEvents);
			float f1 = gEnv->pTimer->GetAsyncCurTime();
			CryLog("<Sound Profile Getting Group> %s %.2f", m_Props.sName.c_str(), (f1-f0)*1000.0f);
		}

		// Pre-cache flag has the same result as using pEventGroup->LoadEventData(EVENT_RESOURCE_STREAMS_AND_SAMPLES);
		if (pEventGroup)
		{
			int nNumEvents = 0;
			m_ExResult = pEventGroup->getNumEvents(&nNumEvents);

			m_EventTimeouts.clear();
			m_EventTimeouts.resize(nNumEvents);
		}
	}

	return (pEventGroup);
}

// Gets and Sets Parameter defined in the enumAssetParam list
//////////////////////////////////////////////////////////////////////////
bool CSoundBufferFmodEx400Event::GetParam(enumAssetParamSemantics eSemantics, ptParam* pParam) const
{
	bool bSuccess = false;

	switch (eSemantics)
	{	
	case apASSETNAME:	
		{
			string sTemp;

			if (pParam->GetValue(sTemp))
			{
				sTemp    = m_Props.sName.c_str();
				bSuccess = pParam->SetValue(sTemp);
			}

			break;
		}
	case apASSETTYPE:	
		{
			int32 nTemp = 0;

			if (pParam->GetValue(nTemp))
			{
				nTemp    = m_Props.eBufferType;
				bSuccess = pParam->SetValue(nTemp);
			}

			break;
		}	
	case apNUMSIMPLESOUNDS:
		{
			int32 nTemp = 0;

			if (pParam->GetValue(nTemp) && m_BufferData)
			{
				FMOD::EventGroup *pEventGroup = (FMOD::EventGroup*)m_BufferData;
				int nNumEvents                = 0;
				int nNumSimple                = 0;

				FMOD_RESULT	ExResult = pEventGroup->getNumEvents(&nNumEvents);

				if (ExResult == FMOD_OK && nNumEvents > 0)
				{
					// We are being positive! :)
					bSuccess = true;

					for (int i = 0; i < nNumEvents; ++i)
					{
						FMOD::Event *pEvent = 0;
						ExResult = pEventGroup->getEventByIndex(i, FMOD_EVENT_INFOONLY, &pEvent);

						if (ExResult == FMOD_OK)
						{
							int nType = 0;
							ExResult = pEvent->getPropertyByIndex(FMOD_EVENTPROPERTY_EVENTTYPE, &nType);

							if (ExResult == FMOD_OK)
							{
								// This is a simple event.
								if (nType == 0)
								{
									++nNumSimple;
								}
							}
							else
							{
								bSuccess = false;

								break;
							}
						}
						else
						{
							bSuccess = false;

							break;
						}
					}

					// Set the value only if the looping above was successful.
					bSuccess = bSuccess ? pParam->SetValue(nNumSimple) : false;
				}
			}
		}
	case apLOADINGSTATE:	
		{
			int32 nTemp = 0;

			if (pParam->GetValue(nTemp))
			{
				nTemp    = m_Info.bLoadFailure;
				bSuccess = pParam->SetValue(nTemp);
			}
			
			break;
		}
	case 	apNUMREFERENCEDSOUNDS:
		{
			int32 nTemp = 0;

			if (pParam->GetValue(nTemp))
			{
				nTemp    = m_vecLoadReq.size();
				bSuccess = pParam->SetValue(nTemp);
			}

			break;
		}
	case apSTATE:
		{
			int32 nTemp = 0;

			if (pParam->GetValue(nTemp))
			{
				// Always set 1 even if there's no "m_BufferData".
				bSuccess = pParam->SetValue(1);

				if (m_BufferData)
				{
					FMOD_EVENT_STATE oEventState;
					FMOD::EventGroup* const pEventGroup = static_cast<FMOD::EventGroup* const>(m_BufferData);
					FMOD_RESULT const eResult           = pEventGroup->getState(&oEventState);
					bSuccess                            = pParam->SetValue((eResult == FMOD_OK) ? 1 : 0);
				}
			}
		}
	default:
		{
			break;
		}
	}

	return bSuccess;
}

//////////////////////////////////////////////////////////////////////////
bool CSoundBufferFmodEx400Event::SetParam(enumAssetParamSemantics eSemantics, ptParam* pParam)
{
	bool bResult = false;

	switch (eSemantics)
	{
	case apASSETNAME:	// TODO Review this, Reset the Asset?
	case apASSETTYPE:	// TODO Review this
		{
			break;
		}
	default:
		{
			break;
		}
	}

	return bResult;
}

//////////////////////////////////////////////////////////////////////////
bool CSoundBufferFmodEx400Event::UnloadData(const eUnloadDataOptions UnloadOption)
{
	UnloadDataIncrementalResult result = UnloadDataIncremental(UnloadOption, 0, (size_t) -1);

	// Success is if we either processed something or don't have anything remaining as this can be called even though the buffer hasn't got anything loaded.
	return result.processed != 0 || result.remaining == 0;
}

ISoundBuffer::UnloadDataIncrementalResult CSoundBufferFmodEx400Event::UnloadDataIncremental(eUnloadDataOptions UnloadOption, size_t unloadBegin, size_t unloadLimit)
{
	FUNCTION_PROFILER(GetISystem(), PROFILE_SOUND);

	//GUARD_HEAP;

	CAudioDeviceFmodEx400 *pAudioDeviceFmodEx400 =  (CAudioDeviceFmodEx400*)m_pSoundSystem->GetIAudioDevice();

	if (m_BufferData)
	{
		if (UnloadOption == sbUNLOAD_ALL_DATA || UnloadOption == sbUNLOAD_ALL_DATA_NOW)
		{
			//FUNCTION_PROFILER( m_pSoundSystem->GetSystem(),PROFILE_SOUND );

			// store processed sounds in a container to prevent racing condition and to delay the release() of the sound
			m_ProtectedSounds.reserve(m_vecLoadReq.size() + m_vecLoadReqToAdd.size());

			// Tell Sounds that their soundbuffers are unloaded now
			for (TBufferLoadReqVecIt It=m_vecLoadReq.begin(); It!=m_vecLoadReq.end(); ++It) 
			{
				ISound* pSound = (*It);
				m_ProtectedSounds.push_back(pSound);
				((CSound*)(*It))->OnAssetUnloaded(); 
			}
			for (TBufferLoadReqVecIt It=m_vecLoadReqToAdd.begin(); It!=m_vecLoadReqToAdd.end(); ++It) 
			{
				ISound* pSound = (*It);
				m_ProtectedSounds.push_back(pSound);
				((CSound*)(*It))->OnAssetUnloaded(); 
			}

			if (m_pSoundSystem->g_nDebugSound == SOUNDSYSTEM_DEBUG_DETAIL)
			{
				m_pSoundSystem->Log(eSLT_Message, "<Sound> Destroying sound buffer %s \n", m_Props.sName.c_str());		
			}

			FMOD::EventGroup *pEventGroup = (FMOD::EventGroup*)m_BufferData;

			if (m_pSoundSystem->g_nDebugSound == SOUNDSYSTEM_DEBUG_RECORD_COMMANDS)
				pAudioDeviceFmodEx400->m_CommandPlayer.LogCommand(CCommandPlayerFmodEx400::GROUP_FREEDATA, (int)gEnv->pTimer->GetFrameStartTime().GetMilliSeconds(), ptParamUINT32((uint32)pEventGroup));

			{
				CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_BUFFER_OTHER) );
				m_ExResult = pEventGroup->freeEventData(0, UnloadOption == sbUNLOAD_ALL_DATA_NOW);
			}

			if (IS_FMODERROR)
			{
				if (m_pSoundSystem->g_nDebugSound == SOUNDSYSTEM_DEBUG_DETAIL)
					m_pSoundSystem->Log(eSLT_Message, "<Sound> Couldn't free event group data! %s \n", m_Props.sName.c_str());

				// FMOD_ERR_NOTREADY might happen if FMOD could not get critical section, we need to try later
				if (m_ExResult != FMOD_ERR_NOTREADY)
					FmodErrorOutput("release event group failed! ", eSLT_Warning);

				m_ProtectedSounds.clear();

				// Inform the unloader that we failed to do any work, and still have 1 thing left to do (unload!) - forcing a retry next frame
				return UnloadDataIncrementalResult(0, 1);
			}

			CSoundSystem::g_nBuffersTrashed++;

			if (m_pSoundSystem->g_nDebugSound == SOUNDSYSTEM_DEBUG_DETAIL)
				m_pSoundSystem->Log(eSLT_Message, "<Sound> Freeing event group data! %s \n", m_Props.sName.c_str());
			//else gEnv->pLog->Log("Error: Sound-FmodEx-SoundBuffer-Event: %s %s \n", m_Props.sName.c_str(), "released succesfully.");

			// Project Refcounting
			SProjectFile* const pProjectFile = pAudioDeviceFmodEx400->GetProjectFile(m_Props.nProjectIndex, true);

			if (pProjectFile)
			{
				assert (pProjectFile->nRefCount > 0);

				if (pProjectFile->nRefCount)
					--pProjectFile->nRefCount;
			}

			switch (m_Props.eBufferType)
			{
			case btSAMPLE:
				{
					SetAssetHandle(NULL, btSAMPLE);
					break;
				}
			case btEVENT:	
				{
					SetAssetHandle(NULL, btEVENT);
					break;
				}
			default:
				{
					SetAssetHandle(NULL, btNONE);
					break;
				}
			}

			m_ProtectedSounds.clear();

			// Make sure to also reset the event timeouts.
			m_EventTimeouts.clear();

			// Inform the unloader that we successfully unloaded one thing, and have no more work to do.
			return UnloadDataIncrementalResult(1, 0);
		}
		else if (UnloadOption == sbUNLOAD_UNNEEDED_DATA || UnloadOption == sbUNLOAD_UNNEEDED_DATA_NOW)
		{
			FMOD::EventGroup *pEventGroup = (FMOD::EventGroup*)m_BufferData;
			CTimeValue tNow = gEnv->pTimer->GetFrameStartTime();

			size_t const nCount = m_EventTimeouts.size();
			size_t unloaded = 0, numLeftToUnload = nCount - unloadBegin;

			for (size_t i = unloadBegin, iMax = unloadBegin + unloadLimit; i < iMax && i < nCount; ++i, ++ unloaded, -- numLeftToUnload)
			{
				FMOD::Event *pEvent = 0;
				{
					CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_BUFFER_OTHER) );
					m_ExResult = pEventGroup->getEventByIndex(i, FMOD_EVENT_INFOONLY, &pEvent);
				}

				if (IS_FMODERROR)
				{
					FmodErrorOutput("get event by index failed! ", eSLT_Warning);

					// Bail out now, pick up again next frame where we left off
					return UnloadDataIncrementalResult(unloaded, numLeftToUnload);
				}

				FMOD_EVENT_INFO EventInfo;
				FMOD_EVENT_STATE oEventState;

				{
					CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_BUFFER_OTHER) );
					m_ExResult = pEvent->getInfo(0,0,&EventInfo);

					if (!IS_FMODERROR)
					{
						m_ExResult = pEvent->getState(&oEventState);
					}
				}

				if (IS_FMODERROR)
				{
					// Make sure to also try and free event data in the event of any error except when the event is still being loaded
					CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_BUFFER_RELEASE) );

					if (m_ExResult != FMOD_ERR_NOTREADY)
						m_ExResult = pEventGroup->freeEventData(pEvent, UnloadOption == sbUNLOAD_UNNEEDED_DATA_NOW);

					FmodErrorOutput("event getInfo or getState failed!", eSLT_Warning);

					// If the freeing of the data was successful nevertheless we need to add this to "unloaded" as we do not finish the loop to have it incremented.
					return UnloadDataIncrementalResult(unloaded + ((m_ExResult == FMOD_OK) ? 1 : 0), numLeftToUnload);
				}

				if (EventInfo.instancesactive == 0 && (oEventState & FMOD_EVENT_STATE_CHANNELSACTIVE) == 0)
				{
					CTimeValue const tDiff(tNow - m_EventTimeouts[i]);
					int const nSeconds = (int)tDiff.GetSeconds();

					if (nSeconds > 2 && (oEventState & FMOD_EVENT_STATE_LOADING) == 0)
					{
						if (m_pSoundSystem->g_nDebugSound == SOUNDSYSTEM_DEBUG_RECORD_COMMANDS)
							pAudioDeviceFmodEx400->m_CommandPlayer.LogCommand(CCommandPlayerFmodEx400::GROUP_FREEEVENTDATA, (int)gEnv->pTimer->GetFrameStartTime().GetMilliSeconds(), ptParamUINT32((uint32)pEventGroup), ptParamUINT32((uint32)i));

						{
							CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_BUFFER_RELEASE) );
							m_ExResult = pEventGroup->freeEventData(pEvent, UnloadOption == sbUNLOAD_UNNEEDED_DATA_NOW);
						}

						if (IS_FMODERROR)
						{
							// Result FMOD_ERR_NOTREADY is an indication of pending load requests in FMOD's event loading queue.
							// Those need to get satisfied first. The load queue literally needs to be empty for freeEventData to work!
							if (m_ExResult != FMOD_ERR_NOTREADY)
								FmodErrorOutput("release event group failed! ", eSLT_Warning);

							return UnloadDataIncrementalResult(unloaded, numLeftToUnload);
						}

						CSoundSystem::g_nBuffersTrashed++;

						m_EventTimeouts[i] = tNow;
					}
				}
				else
				{
					m_EventTimeouts[i] = tNow;
				}
			}

			return UnloadDataIncrementalResult(unloaded, numLeftToUnload);
		}
	}

	return UnloadDataIncrementalResult();
}

bool CSoundBufferFmodEx400Event::NotLoaded() const		
{
	CAudioDeviceFmodEx400* pAudioDeviceFmodEx400 = (CAudioDeviceFmodEx400*)m_pSoundSystem->GetIAudioDevice();
	SProjectFile* const pProjectFile = pAudioDeviceFmodEx400->GetProjectFile(m_Props.nProjectIndex, true);

	if (!pProjectFile 
		|| (pProjectFile->pProjectHandle == PROJECTHANDLE_INVALID && pProjectFile->pReadStream == NULL)
		|| (m_BufferData == NULL && m_pReadStream == NULL))
		return true;
	else
		return false;
}

bool CSoundBufferFmodEx400Event::Loaded(const bool bUpdateTimer) const				
{
	CAudioDeviceFmodEx400* pAudioDeviceFmodEx400 = (CAudioDeviceFmodEx400*)m_pSoundSystem->GetIAudioDevice();
	SProjectFile* const pProjectFile = pAudioDeviceFmodEx400->GetProjectFile(m_Props.nProjectIndex, bUpdateTimer);

	if (pProjectFile && (pProjectFile->pProjectHandle != PROJECTHANDLE_INVALID) 
		&& (pProjectFile->pReadStream == NULL)
		&& (m_BufferData != NULL)
		&& (m_pReadStream == NULL))
		return true;
	else
		return false;
}

bool CSoundBufferFmodEx400Event::Loading() const			
{ 
	CAudioDeviceFmodEx400* pAudioDeviceFmodEx400 = (CAudioDeviceFmodEx400*)m_pSoundSystem->GetIAudioDevice();
	SProjectFile* const pProjectFile = pAudioDeviceFmodEx400->GetProjectFile(m_Props.nProjectIndex, false);

	if ((pProjectFile && (pProjectFile->pProjectHandle == PROJECTHANDLE_INVALID) && ((pProjectFile->pReadStream != NULL) || pProjectFile->pProjectFileInMemory))
		|| m_pReadStream)
		return true;
	else
		return false;
}

bool CSoundBufferFmodEx400Event::LoadFailure() const	
{ 
	CAudioDeviceFmodEx400* pAudioDeviceFmodEx400 = (CAudioDeviceFmodEx400*)m_pSoundSystem->GetIAudioDevice();
	SProjectFile* const pProjectFile = pAudioDeviceFmodEx400->GetProjectFile(m_Props.nProjectIndex, false);

	if (!pProjectFile 
		|| (pProjectFile->pProjectHandle == PROJECTHANDLE_INVALID && pProjectFile->pReadStream == NULL && pProjectFile->pProjectFileInMemory == NULL)
		|| m_Info.bLoadFailure)
		return true;
	else
		return false;
}

//////////////////////////////////////////////////////////////////////////
FMOD::EventGroup* const CSoundBufferFmodEx400Event::GetEventGroup(char const* const pcGroupName, bool const bCacheEvents)
{
	CAudioDeviceFmodEx400* const pAudioDeviceFmodEx400 = (CAudioDeviceFmodEx400*)m_pSoundSystem->GetIAudioDevice();

	SProjectFile* const pProjectFile = pAudioDeviceFmodEx400 ? pAudioDeviceFmodEx400->GetProjectFile(m_Props.nProjectIndex, true) : NULL;
	if (!pProjectFile || pProjectFile->pProjectHandle == PROJECTHANDLE_INVALID)
		return NULL; // Project is not ready

	FMOD::EventProject* pProject	= NULL;
	FMOD::EventGroup* pEventGroup	= NULL;
	FMOD::EventSystem* const pEventSystem = (FMOD::EventSystem*)pAudioDeviceFmodEx400->GetEventSystem();
	
	if (pEventSystem)
	{
		pProject = (FMOD::EventProject*)pProjectFile->pProjectHandle;
		if (pProject)
		{
			if (m_nGroupIndex == INVALID_GROUPID)
			{
				m_ExResult = pProject->getGroup(pcGroupName, bCacheEvents, &pEventGroup);

				if (IS_FMODERROR)
				{
					FmodErrorOutput("get event group by name failed! ", eSLT_Warning);
					return NULL;
				}

				if (pEventGroup)
				{
					// Save the group index once for faster access at later times

					// Deactivate Caching of GroupIndex because GroupIndex of subfolder is not the same as GroupIndex of Project
					//m_ExResult = pEventGroup->getInfo(&m_nGroupIndex, NULL);
				}
			}
			else
			{
				m_ExResult = pProject->getGroupByIndex(m_nGroupIndex, bCacheEvents, &pEventGroup);
				if (IS_FMODERROR)
				{
					FmodErrorOutput("get event group by index failed! ", eSLT_Warning);
					return NULL;
				}
			}
		}
	}

	if (m_pSoundSystem->g_nDebugSound == SOUNDSYSTEM_DEBUG_RECORD_COMMANDS)
		pAudioDeviceFmodEx400->m_CommandPlayer.LogCommand(CCommandPlayerFmodEx400::PROJECT_GETGROUP, (int)gEnv->pTimer->GetFrameStartTime().GetMilliSeconds(), ptParamUINT32((uint32)pProject), ptParamCHARP(pcGroupName), ptParamBOOL(bCacheEvents), ptParamUINT32((uint32)pEventGroup));

	if (pEventGroup)
	{
		if (m_Props.nPrecacheFlags & FLAG_SOUND_PRECACHE_LOAD_GROUP)
		{
			m_ExResult = pEventGroup->loadEventData(FMOD_EVENT_RESOURCE_SAMPLES, FMOD_EVENT_NONBLOCKING);
			if (IS_FMODERROR)
			{
				FmodErrorOutput("event group precaching failed! ", eSLT_Warning);
				return NULL;
			}
		}

		// Project ref-counting
		++pProjectFile->nRefCount;
	}

	return pEventGroup;
}

#endif
